# SPDX-License-Identifier: BSD-2-Clause

import os

from migen import *
from litex.gen import *

from litex.soc.interconnect.csr import *
from litex.soc.integration.doc import AutoDoc, ModuleDoc

#from litex.build.io import SDRTristate
from migen.fhdl.specials import Tristate

# CRC_PCIE -----------------------------------------------------------------------------------------------------

class ONEWIRE(LiteXModule):

    def __init__(self, platform, pads):
        self.intro = ModuleDoc("""Introduction

    Provides a generic ONEWIRE engine core.

    The ONEWIRE engine has 8bit write data input and 8bit read data output, which can be used in ds18b20

    ``wr`` register bit load data input, write 1 to this bit will trigger write transaction.

    ``rd`` register bit read data output, write 1 to this bit will trigger read transaction. 

    """)

        self.pads    = pads

        self._clkdiv = CSRStorage(description="ONEWIRE Clock Divider Value.", fields=[
            CSRField("div", size=8, offset=0, reset=50, description="ONEWIRE engine 8bit Clock Divider."),
        ])

        self._control = CSRStorage(description="ONEWIRE Control.", fields=[
            CSRField("rd", size=1, offset=0, reset=0, description="ONEWIRE engine read data enable."),
            CSRField("ow_rst", size=1, offset=8, reset=0, description="ONEWIRE engine reset enable."),
        ])

        self._datain = CSRStorage(description="ONEWIRE Data Input & Write Enable.", fields=[
            CSRField("data_in", size=8, offset=0, reset=0, description="ONEWIRE engine 8bit data input."),
            CSRField("wr", size=1, offset=8, reset=0, description="ONEWIRE engine write data enable."),
        ])

        self._dataout = CSRStatus(description="ONEWIRE Data Output.", fields=[
            CSRField("data_out", size=8, offset=0, description="ONEWIRE engine 8bit data output."),
            CSRField("ready", size=1, offset=8, description="ONEWIRE engine ready status output."),
        ])

        # ONEWIRE IOs.
        onewire_ios = Record([
            ("dq_i", 1), ("dq_o", 1), ("dq_oe", 1),
        ])


        # # #

        # crc_top Verilog Core Instance.
        self.specials += Instance(self.get_netlist_name(),
            # Clk / Rst.
            i_clk     = ClockSignal("sys"),
            #i_reset   = ResetSignal("sys"),
            # Control and Data
            i_div     = self._clkdiv.fields.div,
            i_read    = self._control.fields.rd,
            i_reset   = self._control.fields.ow_rst,
            i_wrdata  = self._datain.fields.data_in,
            i_write   = self._datain.fields.wr,
            o_rddata  = self._dataout.fields.data_out,
            o_ready   = self._dataout.fields.ready,
            # pads.
            i_dq_i    = onewire_ios.dq_i,
            o_dq_o    = onewire_ios.dq_o,
            o_dq_oe   = onewire_ios.dq_oe,
        )

        #self.specials += SDRTristate(
        #    io  = pads.dqwire,
        #    o   = onewire_ios.dq_o,
        #    oe  = onewire_ios.dq_oe,
        #    i   = onewire_ios.dq_i,
        #    #clk = ClockSignal("usb"),
        #)

        self.specials += Tristate(pads.dqwire,
            o  = onewire_ios.dq_o,  # I2C uses Pull-ups, only drive low.
            oe = onewire_ios.dq_oe, # Drive when oe and sda is low.
            i  = onewire_ios.dq_i,
        )


        self.add_sources(platform)


    def get_netlist_name(self):
        return "onewire_top"

    def add_sources(self, platform):
        cdir = os.path.abspath('.')
        vdir = os.path.join(cdir, "ONEWIRE", "sim")
        netlist_name = self.get_netlist_name()

        print(f"ONEWIRE netlist : {netlist_name}")
        if not os.path.exists(os.path.join(vdir, netlist_name + ".v")):
            self.generate_netlist()

        platform.add_source(os.path.join(vdir,  netlist_name + ".v"), "verilog")

    def generate_netlist(self):
        print(f"Generating onewire_top netlist")
        sources = []
        sdir = "ONEWIRE"
        if not os.path.exists(sdir):
            os.system(f"git clone git://repogit.moditek/onewire.git ONEWIRE")

        cdir = os.path.abspath('.')

        cmd = 'cd {path} && bash merge_rtl.cmd'.format(
            path=os.path.join(cdir, "ONEWIRE", "sim") )
        print("!!! "   + cmd)
        if os.system(cmd) != 0:
            raise OSError('Failed to run merge_rtl.cmd')

